package org.wu.framework.wuconfig2mdplusstarter;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.util.ObjectUtils;
import org.wu.framework.core.CamelAndUnderLineConverter;
import org.wu.framework.core.NormalUsedString;
import org.wu.framework.core.utils.FileUtil;

import java.io.BufferedWriter;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;

/**
 * ConfigurationProperties2MdPostProcessor
 * DDL 操作
 *
 * @author : Jia wei Wu
 * @version 1.0
 * @date : 2022/1/2 6:23 下午
 */
@Slf4j
public class ConfigurationProperties2MdPostProcessor implements BeanPostProcessor {

    /**
     * Apply this {@code BeanPostProcessor} to the given new bean instance <i>before</i> any bean
     * initialization callbacks (like InitializingBean's {@code afterPropertiesSet}
     * or a custom init-method). The bean will already be populated with property values.
     * The returned bean instance may be a wrapper around the original.
     * <p>The default implementation returns the given {@code bean} as-is.
     *
     * @param bean     the new bean instance
     * @param beanName the name of the bean
     * @return the bean instance to use, either the original or a wrapped one;
     * if {@code null}, no subsequent BeanPostProcessors will be invoked
     * @throws BeansException in case of errors
     * @see InitializingBean#afterPropertiesSet
     */
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {


        Class<?> beanClass = bean.getClass();
        String beanClassName = beanClass.getName();
        try {
            // 获取含有配置注解的对象
            if (beanClass.getAnnotation(ConfigurationProperties.class) != null
//                    && beanClassName.startsWith("org.wu")
            ) {

                StringBuilder stringBuilder = new StringBuilder();
                File file = FileUtil.createUserDirFile("ConfigurationProperties2Md.md");
                String readFileContent0 = FileUtil.readFileContent0(file);
                stringBuilder.append(readFileContent0);

                ConfigurationProperties configurationProperties = beanClass.getAnnotation(ConfigurationProperties.class);
                String prefix = configurationProperties.prefix();
                String value = configurationProperties.value();
                String keyPrefix = ObjectUtils.isEmpty(prefix) ? value : prefix;

                /**
                 *
                 | 模块                                         | 所属层级 | 描述                                |
                 |--------------------------------------------|------|-----------------------------------|
                 | wu-database-lazy-orm-mybatis-cure-plus-starter | 插件   | 针对mybatis框架存在字段不存在、表不存在，进行数据库自动治愈 |
                 | wu-database-lazy-plus-starter              | 插件   | lazy插件                            |
                 | wu-database-plus                           | 插件   | 弃用                                |

                 */
                stringBuilder.append("## ");
                stringBuilder.append(beanClassName);
                stringBuilder.append(NormalUsedString.NEWLINE);
                stringBuilder.append("| key   | 字段类型 | 默认数据                                | 配置描述 |");
                stringBuilder.append(NormalUsedString.NEWLINE);
                stringBuilder.append("|--------------------------------------------|------|-----------------------------------|-----------------------------------|");
                stringBuilder.append(NormalUsedString.NEWLINE);

                for (Field declaredField : beanClass.getDeclaredFields()) {
                    declaredField.setAccessible(true);
                    Object declaredFieldValue = declaredField.get(bean);
                    String defaultValue = declaredFieldValue == null ? "" : declaredFieldValue.toString();
                    String name = CamelAndUnderLineConverter.humpToMidLine2(declaredField.getName());
                    stringBuilder.append(String.format("| %s.%s   | %s | %s |  |",
                            keyPrefix, name,
                            declaredField.getType().getName(),
                            defaultValue));
                    stringBuilder.append(NormalUsedString.NEWLINE);
                }

                stringBuilder.append(NormalUsedString.NEWLINE);

                // 写数据
                BufferedWriter fileBufferedWriter = FileUtil.createFileBufferedWriter(file);
                try {
                    fileBufferedWriter.write(stringBuilder.toString());
                } catch (IOException e) {
                    throw new RuntimeException(e);
                } finally {
                    try {
                        fileBufferedWriter.close();
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }


        return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
    }

    /**
     * Apply this {@code BeanPostProcessor} to the given new bean instance <i>after</i> any bean
     * initialization callbacks (like InitializingBean's {@code afterPropertiesSet}
     * or a custom init-method). The bean will already be populated with property values.
     * The returned bean instance may be a wrapper around the original.
     * <p>In case of a FactoryBean, this callback will be invoked for both the FactoryBean
     * instance and the objects created by the FactoryBean (as of Spring 2.0). The
     * post-processor can decide whether to apply to either the FactoryBean or created
     * objects or both through corresponding {@code bean instanceof FactoryBean} checks.
     * <p>This callback will also be invoked after a short-circuiting triggered by a
     * {@link InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation} method,
     * in contrast to all other {@code BeanPostProcessor} callbacks.
     * <p>The default implementation returns the given {@code bean} as-is.
     *
     * @param bean     the new bean instance
     * @param beanName the name of the bean
     * @return the bean instance to use, either the original or a wrapped one;
     * if {@code null}, no subsequent BeanPostProcessors will be invoked
     * @throws BeansException in case of errors
     * @see InitializingBean#afterPropertiesSet
     * @see FactoryBean
     */
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
    }


}
